package tests.ge;

import ge.BitsIterator;
import ge.ExpressionEvaluator;

import java.util.Arrays;
import java.util.Iterator;

public class Tests {
	private static Boolean[][] all_inputs;
	private static Boolean[] all_targets;

	public static void main(String[] args) {
		timeTestMultiplexer();
	}

	public static void timeTestMultiplexer() {
		
		int  num_select_bits = 3, num_loops = 5000;
		long start, end;
		start = System.currentTimeMillis();
		for (int i = 0; i < num_loops; i++) {
			testMultiplexer(num_select_bits);
		}
		end = System.currentTimeMillis();
		System.out.println("loop: " + (end - start));

		start = System.currentTimeMillis();
		makeTables(num_select_bits);
		for (int i = 0; i < num_loops; i++) {
			precalcMultiplexer(num_select_bits);
		}
		end = System.currentTimeMillis();
		System.out.println("lookup: " + (end - start));
		
	}

	private static void makeTables(int num_select_bits) {
		int num_bits = num_select_bits + ((int) Math.pow(2, num_select_bits));
		all_inputs = new Boolean[(int) Math.pow(2, num_bits)][];
		all_targets = new Boolean[(int) Math.pow(2, num_bits)];
		
		Boolean[] inputs = new Boolean[num_bits];
		Arrays.fill(inputs, false);
		for (int i = 0; i < Math.pow(2, inputs.length); i++) {
			boolean carry = true;
			for (int j = num_bits - 1; j >= 0 && carry; j--) {
				if (carry) {
					carry = inputs[j];
					inputs[j] = !inputs[j];
				}
			}
			
			all_inputs[i] = new Boolean[num_bits];
			System.arraycopy(inputs, 0, all_inputs[i], 0, num_bits);

			// get the value of the select bits
			int selection = 0;
			for (int idx = 0; idx < num_select_bits; idx++) {
				selection += (inputs[idx] ? (int) Math.pow(2, idx) : 0);
			}

			// get the selected value
			all_targets[i] = inputs[num_select_bits + selection];

		}
		
	}

	private static void precalcMultiplexer(int num_select_bits) {
		Class<Boolean> return_type = Boolean.class;

		int num_bits = num_select_bits + ((int) Math.pow(2, num_select_bits));

		String[] parameter_names = new String[num_bits];

		Class<?>[] parameter_types = new Class<?>[num_bits];

		for (int i = 0; i < num_bits; i++) {
			String prefix = "s";
			int idx = i + 1;
			if (i >= num_select_bits) {
				prefix = "i";
				idx -= num_select_bits;
			}
			parameter_names[i] = prefix + idx;
			parameter_types[i] = boolean.class;
		}

		String code = "((!s1 & !s2 & !s3) & i1) | ((s1 & !s2 & !s3) & i2) | ((!s1 & s2 & !s3) & i3) | ((s1 & s2 & !s3) & i4) | ((!s1 & !s2 & s3) & i5) | ((s1 & !s2 & s3) & i6) | ((!s1 & s2 & s3) & i7) | ((s1 & s2 & s3) & i8)";

		ExpressionEvaluator ee = null;
		try {
			ee = new ExpressionEvaluator(code, return_type, parameter_names,
					parameter_types);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		double fitness = 0;
		for (int i = 0; i < all_inputs.length; i++) {
			
			// run the solution on the inputs
			boolean solution_output = false;
			try {
				solution_output = (Boolean) ee.execute(all_inputs[i]);
			} catch (Exception e) {
			}

			if (solution_output != all_targets[i]) {
				fitness -= 1;
			}

		}

		
	}

	public static void testMultiplexer(int num_select_bits) {
		Class<Boolean> return_type = Boolean.class;

		int num_bits = num_select_bits + ((int) Math.pow(2, num_select_bits));

		String[] parameter_names = new String[num_bits];

		Class<?>[] parameter_types = new Class<?>[num_bits];

		for (int i = 0; i < num_bits; i++) {
			String prefix = "s";
			int idx = i + 1;
			if (i >= num_select_bits) {
				prefix = "i";
				idx -= num_select_bits;
			}
			parameter_names[i] = prefix + idx;
			parameter_types[i] = boolean.class;
		}

		String code = "((!s1 & !s2 & !s3) & i1) | ((s1 & !s2 & !s3) & i2) | ((!s1 & s2 & !s3) & i3) | ((s1 & s2 & !s3) & i4) | ((!s1 & !s2 & s3) & i5) | ((s1 & !s2 & s3) & i6) | ((!s1 & s2 & s3) & i7) | ((s1 & s2 & s3) & i8)";
		// code ="((!s1 & !s2) & i1) | ((s1 & !s2) & i2) | ((!s1 & s2) & i3) |
		// ((s1 & s2) & i4)";
		// code ="(!s1 & ((!s2 & i1) ^ (s2 & i3))) | (s1 & ((!s2 & i2) ^ (s2 &
		// i4)))";
		// code = "(!s1 & ((!s2 & ((!s3 & i1) ^ (s3 & i5))) ^ ((s2 & ((!s3 & i3)
		// ^ (s3 &i7)))))) | (s1 & ((!s2 & ((!s3 & i2) ^ (s3 & i6))) ^ (s2 &
		// ((!s3 & i4) ^ (s3 & i8)))))";
		// code = "(s1 ? (s2 ? i4 : i2) : (s2 ? i3 : i1))";

		ExpressionEvaluator ee = null;
		try {
			ee = new ExpressionEvaluator(code, return_type, parameter_names,
					parameter_types);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		Boolean[] inputs = new Boolean[num_bits];
		Arrays.fill(inputs, false);
		double fitness = 0;
		for (int i = 0; i < Math.pow(2, inputs.length); i++) {
			boolean carry = true;
			for (int j = num_bits - 1; j >= 0 && carry; j--) {
				if (carry) {
					carry = inputs[j];
					inputs[j] = !inputs[j];
				}
			}

			// run the solution on the inputs
			boolean solution_output = false;
			try {
				solution_output = (Boolean) ee.execute(inputs);
			} catch (Exception e) {
			}

			// get the value of the select bits
			int selection = 0;
			for (int idx = 0; idx < num_select_bits; idx++) {
				selection += (inputs[idx] ? (int) Math.pow(2, idx) : 0);
			}

			// get the selected value
			boolean target = inputs[num_select_bits + selection];

			if (solution_output != target) {
				fitness -= 1;
			}

		}

		//System.out.println(fitness);

	}

	public static void timeIterateBits() {
		int num_bits = 10, num_loops = 10000;
		long start, end;
		start = System.currentTimeMillis();
		for (int i = 0; i < num_loops; i++) {
			iterateBitsCarry(num_bits);
		}
		end = System.currentTimeMillis();
		System.out.println("carry: " + (end - start));

		start = System.currentTimeMillis();
		for (int i = 0; i < num_loops; i++) {
			iterateBitsCarryB(num_bits);
		}
		end = System.currentTimeMillis();
		System.out.println("carryB: " + (end - start));

		start = System.currentTimeMillis();
		for (int i = 0; i < num_loops; i++) {
			iterateBitsIterator(num_bits);
		}
		end = System.currentTimeMillis();
		System.out.println(" iter: " + (end - start));
	}

	public static void iterateBitsIterator(int num_bits) {
		Iterator<Boolean[]> iter = new BitsIterator(num_bits);
		for (int i = 0; i < Math.pow(2, num_bits); i++) {
			iter.next();
		}
	}

	public static void iterateBitsWeird(int num_bits) {
		boolean[] inputs = new boolean[num_bits];
		int tmp, pos;
		for (int i = 0; i < Math.pow(2, num_bits); i++) {
			tmp = i;

			for (int j = inputs.length - 1; j >= 0; j--) {
				pos = (int) Math.pow(2, j);
				inputs[j] = ((int) (tmp / pos) == 0 ? false : true);
				tmp -= (inputs[j] ? 1 : 0) * pos;
			}
		}
	}

	public static void iterateBitsCarry(int num_bits) {
		boolean[] inputs = new boolean[num_bits];
		for (int i = 0; i < Math.pow(2, num_bits); i++) {
			boolean carry = true;
			for (int j = num_bits - 1; j >= 0 && carry; j--) {
				if (carry) {
					carry = inputs[j];
					inputs[j] = !inputs[j];
				}
			}
		}
	}

	public static void iterateBitsCarryB(int num_bits) {
		Boolean[] inputs = new Boolean[num_bits];
		Arrays.fill(inputs, Boolean.FALSE);
		for (int i = 0; i < Math.pow(2, num_bits); i++) {
			boolean carry = true;
			for (int j = num_bits - 1; j >= 0 && carry; j--) {
				if (carry) {
					carry = inputs[j].booleanValue();
					inputs[j] = (inputs[j] ? Boolean.FALSE : Boolean.TRUE);
				}
			}
		}
	}
}
